﻿package net.wangit.jdbc.config;

import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.Map;

import net.wangit.framework.config.BeanFactory;
import net.wangit.framework.config.Domains;
import net.wangit.framework.exception.ConfigurationException;
import net.wangit.jdbc.JDBCException;
import net.wangit.jdbc.connection.ConnectionProvider;
import net.wangit.jdbc.connection.ConnectionProviderFactory;
import net.wangit.jdbc.database.Database;
import net.wangit.jdbc.database.HSQL;
import net.wangit.util.Checker;
import net.wangit.util.XMLUtil;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.dom4j.Document;


public class JDBCSettingsFactory {

	private static final Log log = LogFactory.getLog(JDBCSettingsFactory.class);
	private static Map<String, JDBCSettings> jsList = new HashMap<String, JDBCSettings>();

	private JDBCSettingsFactory() {
	}

	public static JDBCSettings getJDBCSettings(String path)
			throws JDBCException {
		JDBCSettings js = jsList.get(path);
		if (js != null) {
			return js;
		} else {
			js = buildJDBCSettings(path);
			jsList.put(path, js);
			return js;
		}
	}

	private static JDBCSettings buildJDBCSettings(String path)
			throws JDBCException {
		org.dom4j.Element root;
		Database database;
		boolean debug;
		String defaultSchema;
		ConnectionProvider provider;
		int fetchSize;
		int batchSize;
		boolean metaSupportsScrollable;
		Document document = XMLUtil.parseByPath(path);
		if (document == null) {
			throw new ConfigurationException((new StringBuilder(path)).append(
					" not found").toString());
		}
		root = document.getRootElement();
		String databaseName = XMLUtil.getAttributeString(root, "name", null);
		if (Checker.isEmpty(databaseName)) {
			log.error("database name is null");
			throw new ConfigurationException(path, "database");
		}
		try {
			database = BeanFactory.getDatabase(databaseName);
		} catch (Exception e) {
			throw new ConfigurationException(path, "database");
		}
		debug = !(database instanceof HSQL) || !Domains.isSystemDomain("dev");
		defaultSchema = XMLUtil
				.getAttributeString(root, "default-schema", null);
		provider = ConnectionProviderFactory.getConnectionProvider(path, root);
		fetchSize = 0;
		try {
			fetchSize = XMLUtil.getTextInt(root, "fetch-size", -1);
		} catch (Exception e) {
			throw new ConfigurationException(path, "fetch-size");
		}
		batchSize = 0;
		try {
			batchSize = XMLUtil.getTextInt(root, "batch-size", 15);
		} catch (Exception e) {
			throw new ConfigurationException(path, "batch-size");
		}
		metaSupportsScrollable = false;
		Connection conn = null;
		try {
			conn = provider.getConnection();
			DatabaseMetaData meta = conn.getMetaData();
			metaSupportsScrollable = meta
					.supportsResultSetType(ResultSet.TYPE_SCROLL_INSENSITIVE);
			if (batchSize > 0 && !meta.supportsBatchUpdates())
				batchSize = 0;
		} catch (SQLException sqle) {
			try {
				provider.closeConnection(conn);
			} catch (SQLException e) {
			}
			log.warn("Could not obtain connection metadata", sqle);
		}
		boolean scrollable = true;
		try {
			scrollable = XMLUtil.getTextBoolean(root, "scrollable",
					metaSupportsScrollable);
		} catch (Exception e) {
			throw new ConfigurationException(path, "scrollable");
		}
		log.info((new StringBuilder("JDBC 2 fetch size: ")).append(fetchSize)
				.toString());
		log.info((new StringBuilder("JDBC 2 batch size: ")).append(batchSize)
				.toString());
		log.info((new StringBuilder("Use scrollable result sets: ")).append(
				scrollable).toString());
		JDBCSettings js = new JDBCSettings();
		js.setDefaultSchema(defaultSchema);
		js.setDebugEnabled(debug);
		js.setDatabase(database);
		js.setConnectionProvider(provider);
		js.setFetchSize(fetchSize);
		js.setBatchSize(batchSize);
		js.setScrollable(scrollable);
		log.info((new StringBuilder("init datasource: ")).append(path)
				.toString());
		return js;
	}

}
